/**
 * 
 */
package precision;

import org.apfloat.Apfloat;

/**
 * Floating point number with arbitrary precision
 * @author Alex J. Bush
 *
 */
public class Arbitrary implements FPObj {
	
	/**
	 * Internal representation of our arbitrary precision
	 */
	Apfloat value;
	
	/**
	 * Precision of the representation, the number will be accurate to
	 * this many significant figures
	 */
	long precision;
	
	/**
	 * Construct a new arbitrary precision object with the defined precision
	 * given as a number of significant figures 
	 * @param value Number to represent
	 * @param precision Number of significant figures to represent
	 */
	public Arbitrary(int value, long precision)
	{
		this.value = new Apfloat(value,precision);
		this.precision = precision;
	}
	
	/**
	 * Construct a new arbitrary precision object with the defined precision
	 * given as a number of significant figures 
	 * @param value Number to represent
	 * @param precision Number of significant figures to represent
	 */
	public Arbitrary(Apfloat value, long precision)
	{
		this.value = value;
		this.precision = precision;
	}

	/**
	 * Ensure both objects are of the same class
	 * @param b FPObj to test <cod>this</code> against
	 */
	private void checkClass(FPObj b)
	{
		if ( this.getClass() != b.getClass() )
		{
			throw new RuntimeException("Mismatched FPObj types; was expecting " + this.getClass() +
					" and recieved " + b.getClass()+ ".");
		}
	}
	
	/**
	 * Return the internal floating point number this object represents
	 * @return
	 */
	public Apfloat getValue()
	{
		return value;
	}
	
	/* (non-Javadoc)
	 * @see precision.FPObj#add(precision.FPObj)
	 */
	@Override
	public FPObj add(FPObj b) {
		checkClass(b);
		return new Arbitrary(value.add(((Arbitrary)b).getValue()),precision);
	}

	/* (non-Javadoc)
	 * @see precision.FPObj#divide(precision.FPObj)
	 */
	@Override
	public FPObj divide(FPObj b) {
		checkClass(b);
		return new Arbitrary(value.divide(((Arbitrary)b).getValue()),precision);
	}

	/* (non-Javadoc)
	 * @see precision.FPObj#greaterThan(precision.FPObj)
	 */
	@Override
	public boolean greaterThan(FPObj b) {
		checkClass(b);
		int r = value.compareTo(((Arbitrary)b).getValue());
		if ( r == 1)
			return true;
		return false;
	}

	/* (non-Javadoc)
	 * @see precision.FPObj#multi(precision.FPObj)
	 */
	@Override
	public FPObj multi(FPObj b) {
		checkClass(b);
		return new Arbitrary(value.multiply(((Arbitrary)b).getValue()),precision);
	}

	/* (non-Javadoc)
	 * @see precision.FPObj#newFPObj()
	 */
	@Override
	public FPObj newFPObj() {
		return new Arbitrary(0, precision);
	}

	/* (non-Javadoc)
	 * @see precision.FPObj#newFPObj(int)
	 */
	@Override
	public FPObj newFPObj(int a) {
		return new Arbitrary(a, precision);
	}

	/* (non-Javadoc)
	 * @see precision.FPObj#sub(precision.FPObj)
	 */
	@Override
	public FPObj sub(FPObj b) {
		checkClass(b);
		return new Arbitrary(value.subtract(((Arbitrary)b).getValue()),precision);
	}

	/* (non-Javadoc)
	 * @see precision.FPObj#toFloat()
	 */
	@Override
	public float toFloat() {
		// TODO Auto-generated method stub
		return value.floatValue();
	}
	
	@Override
	public String toString()
	{
		return value.toString(true);
	}

	@Override
	public FPObj abs() {
		if (value.floatValue() < 0)
			return new Arbitrary(value.negate(),precision);
		else
			return new Arbitrary(value,precision);
	}

	@Override
	public String toType(){
		return "Arbitrary Precision ("+precision+")";
	}
	
}
